#include <ximera/graphics/ogre/camera.hpp>
#include <ximera/graphics/ogre/ogre.hpp>

namespace ximera { namespace graphics { namespace ogre
{
    RenderMode ogreToXimera(Ogre::PolygonMode type)
    {
        switch (type)
        {
        case Ogre::PM_POINTS:  return RenderMode::Points;
        case Ogre::PM_WIREFRAME:  return RenderMode::Wireframe;
        case Ogre::PM_SOLID:  return RenderMode::Solid;
        default: return RenderMode::Solid;
        }
    }

    Ogre::PolygonMode ximeraToOgre(RenderMode type)
    {
        switch (type.value)
        {
        case RenderMode::Solid:  return Ogre::PM_SOLID;
        case RenderMode::Wireframe:  return Ogre::PM_WIREFRAME;
        case RenderMode::Points:  return Ogre::PM_POINTS;
        default: return Ogre::PM_SOLID;
        }
    }

    // -----------------------------------------------------------------------

    Camera::Camera(Ogre::SceneManager* sceneManager, String const& name)
        : ICamera(name),
          _ogreCamera(0),
          _ogreSceneManager(sceneManager)
    {
        XIMERA_ASSERT_DEF(sceneManager);

        try
        {
            _ogreCamera = _ogreSceneManager->createCamera(string_util::wstr2str(name));
            _ogreCamera->setPosition(0, 0, 0);
            _ogreCamera->setNearClipDistance(5);
        }
        catch (Ogre::Exception& e)
        {
            XIMERA_THROW_OGRE_EXCEPTION(e);
        }
    }

    Camera::~Camera()
    {
        _ogreSceneManager->destroyCamera(_ogreCamera);
    }

    math::Vector3 Camera::position() const
    {
        return math::Vector3(_ogreCamera->getPosition().ptr());
    }
    
    void Camera::position(math::Vector3 const& pos)
    {
        _ogreCamera->setPosition(Ogre::Vector3(pos.array()));
    }    

    math::Quaternion Camera::orientation() const
    {
        return math::Quaternion();
    }

    void Camera::orientation(math::Quaternion const& quat)
    {
    }

    RenderMode Camera::renderMode() const
    {
        return ogreToXimera(_ogreCamera->getPolygonMode());
    }

    void Camera::renderMode(RenderMode mode)
    {
        _ogreCamera->setPolygonMode(ximeraToOgre(mode));
    }

    math::Vector3 Camera::direction() const
    {
        return math::Vector3(_ogreCamera->getDirection().ptr());
    }

    void Camera::direction(math::Vector3 const& dir)
    {
        _ogreCamera->setDirection(dir.x, dir.y, dir.z);
    }

    math::Vector3 Camera::up() const
    {
        return math::Vector3(_ogreCamera->getUp().ptr());
    }

    math::Vector3 Camera::right() const
    {
        return math::Vector3(_ogreCamera->getRight().ptr());
    }

    void Camera::lookAt(math::Vector3 const& point)
    {
        _ogreCamera->lookAt(point.x, point.y, point.z);
    }

    Real Camera::aspectRatio() const
    {
        return _ogreCamera->getAspectRatio();
    }

    void Camera::aspectRatio(Real ratio)
    {
        _ogreCamera->setAspectRatio(ratio);
    }

    Real Camera::nearClipDistance() const
    {
        return _ogreCamera->getNearClipDistance();
    }

    void Camera::nearClipDistance(Real distance)
    {
        _ogreCamera->setNearClipDistance(distance);
    }

    Real Camera::farClipDistance() const
    {
        return _ogreCamera->getFarClipDistance();
    }

    void Camera::farClipDistance(Real distance)
    {
        _ogreCamera->setFarClipDistance(distance);
    }

    Real Camera::fov() const
    {
        return _ogreCamera->getFOVy().valueRadians();
    }

    void Camera::fov(Real fov)
    {
        _ogreCamera->setFOVy(Ogre::Radian(fov));
    }

    void Camera::roll(Real angle)
    {
        _ogreCamera->roll(Ogre::Radian(angle));
    }

    void Camera::yaw(Real angle)
    {
        _ogreCamera->yaw(Ogre::Radian(angle));
    }

    void Camera::pitch(Real angle)
    {
        _ogreCamera->pitch(Ogre::Radian(angle));
    }

    void Camera::rotate(math::Vector3 const& angles)
    {   
        pitch(angles.x);
        yaw(angles.y);
        roll(angles.z);        
    }
}}}